package com.ruoyi.produce.process.scheduling.service;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.constant.ERPConstant;
import com.ruoyi.machine.base.device.mapper.DeviceMapper;
import com.ruoyi.produce.base.bom.domain.ProductionMachine;
import com.ruoyi.produce.base.bom.mapper.BomMapper;
import com.ruoyi.produce.base.shift.service.IProduceShiftInfoService;
import com.ruoyi.produce.process.scheduling.domain.MachineScheduling;
import com.ruoyi.produce.process.scheduling.mapper.MachineSchedulingMapper;
import com.ruoyi.produce.process.scheduling.mapper.SchedulingMapper;
import com.ruoyi.produce.process.scheduling.util.SchedulingUtil;
import com.ruoyi.produce.process.worksheet.domain.ProduceWorkSheet;
import com.ruoyi.produce.process.worksheet.mapper.ProduceWorkSheetMapper;
import com.ruoyi.utils.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.*;

@Service
public class MachineSchedulingServiceImpl implements IMachineSchedulingService {
    @Autowired
    private MachineSchedulingMapper machineSchedulingMapper;
    @Autowired
    private SchedulingMapper schedulingMapper;
    @Autowired
    private BomMapper bomMapper;
    @Autowired
    private ProduceWorkSheetMapper workSheetMapper;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IProduceShiftInfoService shiftInfoService;

    /**
     * 查询所有设备的排产记录
     *
     * @param machineScheduling
     * @return
     */
    @Override
    public List<MachineScheduling> selectMachineSchedulingList(MachineScheduling machineScheduling) {
        List<MachineScheduling> machineSchedulingList = machineSchedulingMapper.selectMachineSchedulingList(machineScheduling);
        String shiftInfoId = shiftInfoService.createShiftInfoId();
        for (MachineScheduling item : machineSchedulingList) {
            setIfUsingForObject(item, item.getWorkSheetId(), shiftInfoId);
        }
        return machineSchedulingList;
    }

    /**
     * 根据设备查询其排产记录
     *
     * @param machineId
     * @return
     */
    @Override
    public List<MachineScheduling> selectMachineSchedulingListByMachineId(String machineId) {
        List<MachineScheduling> machineSchedulingList = machineSchedulingMapper.selectMachineSchedulingListByMachineId(machineId);
        String shiftInfoId = shiftInfoService.createShiftInfoId();
        for (MachineScheduling item : machineSchedulingList) {
            setIfUsingForObject(item, item.getWorkSheetId(), shiftInfoId);
        }
        return machineSchedulingList;
    }

//    /**
//     * 新增
//     * @param machineScheduling
//     * @return
//     */
//    @Override
//    public int insertMachineScheduling(MachineScheduling machineScheduling) {
//        return machineSchedulingMapper.insertMachineScheduling(machineScheduling);
//    }

    /**
     * 修改
     *
     * @param machineScheduling
     * @return
     */
    @Override
    public int updateMachineScheduling(MachineScheduling machineScheduling) {
        return machineSchedulingMapper.updateMachineScheduling(machineScheduling);
    }

    /**
     * 删除
     *
     * @param machineScheduling
     * @return
     */
    @Override
    public int deleteMachineScheduling(MachineScheduling machineScheduling) {
        return machineSchedulingMapper.deleteMachineScheduling(machineScheduling);
    }

    /**
     * 自动排产
     *
     * @param worksheetIds
     * @return
     */
    @Override
    public List<MachineScheduling> automaticScheduling(String[] worksheetIds) {
        List<MachineScheduling> tempList = new ArrayList<>();
        for (int i = 0; i < worksheetIds.length; i++) {
            ProduceWorkSheet produceWorkSheet = workSheetMapper.selectProduceWorkSheetById(worksheetIds[i]);
            MachineScheduling machineScheduling = new MachineScheduling(); // 每个加工单一个机器排产对象
            machineScheduling.setWorkSheetId(worksheetIds[i]);
            List<ProductionMachine> deviceList = bomMapper.selectMachineList(produceWorkSheet.getProduceId()); //通过加工单查询产品可以生产的机器
            if (deviceList.isEmpty()) {
                return null; // 没有bom结构
            } else {
                // 先检查有没有空闲设备
                String idleMachineId = findIdleMachine(deviceList);
                if (!idleMachineId.equals("null")) {
                    machineScheduling.setMachineId(idleMachineId);
                    machineScheduling.setMachineName(deviceMapper.selectDeviceById(idleMachineId).getMachineName());
                    machineScheduling.setSchedulingOrder(ERPConstant.SCHEDULING_MAX_PRIORITY);
                } else {
                    // 找天数最合适的那一台机器
                    Map<String, Long> frequency = findSuitableMachine(deviceList);
                    long machineOfMinValue = findMachineOfMinValue(frequency); //找到设备中带生产天数的最小值
                    String machineId = getKey(frequency, machineOfMinValue); //找到对应的设备
                    machineScheduling.setMachineId(machineId);
                    machineScheduling.setMachineName(deviceMapper.selectDeviceById(machineId).getMachineName());
                    machineScheduling.setSchedulingOrder(machineSchedulingMapper.searchSchedulingMaxOrderByMachineId(machineId).getSchedulingOrder() + 1);
                }
            }
            if (checkIfScheduled(worksheetIds[i])) {
                // 已经有排产记录，不需要添加到返回列表中
                continue;
            }
            tempList.add(machineScheduling);
        }

        return tempList;
    }

    /**
     * 确认排产
     *
     * @param machineSchedulingList
     * @return
     */
    @Override
    public int confirmScheduling(List<MachineScheduling> machineSchedulingList) {
        try {
            return releaseScheduling(machineSchedulingList);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 根据加工单编号查询该加工单可以下放的设备
     *
     * @param workSheetId
     * @return
     */
    @Override
    public List<ProductionMachine> searchMachineByWorkSheetId(String workSheetId) {
        ProduceWorkSheet produceWorkSheet = workSheetMapper.selectProduceWorkSheetById(workSheetId);
        return bomMapper.selectMachineList(produceWorkSheet.getProduceId());
    }

    /**
     * 找到空闲设备
     *
     * @param deviceList 某产品可生产的设备列表
     * @return
     */
    public String findIdleMachine(List<ProductionMachine> deviceList) {
        for (ProductionMachine device : deviceList) {
            List<MachineScheduling> machineSchedulingList = machineSchedulingMapper.selectMachineSchedulingListByMachineId(device.getMachineId());
            if (machineSchedulingList.isEmpty()) {
                return device.getMachineId();
            }
        }
        return "null";
    }

    /**
     * 找到合适生产的机器，返回一个哈希表
     *
     * @param deviceList
     * @return
     */
    public Map<String, Long> findSuitableMachine(List<ProductionMachine> deviceList) {
        Map<String, Long> frequency = new HashMap<>();
        for (ProductionMachine device : deviceList) {
            List<MachineScheduling> machineSchedulingList = machineSchedulingMapper.selectMachineSchedulingListByMachineId(device.getMachineId());
            long timeSum = 0;
            for (MachineScheduling item : machineSchedulingList) {
                // 查询该设备上所有的加工单的预计完成时间，并计算总值
                ProduceWorkSheet workSheet = workSheetMapper.selectProduceWorkSheetById(item.getWorkSheetId());
                Timestamp now = new Timestamp(new Date().getTime());
                long time = TimeUtil.TimeSubtraction(workSheet.getPredictedFinishingTime(), now);
                timeSum += time;
            }
            frequency.put(device.getMachineId(), timeSum);
        }
        return frequency;
    }

    /**
     * 将排产计划添加到数据库中
     *
     * @param machineSchedulingList
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public int releaseScheduling(List<MachineScheduling> machineSchedulingList) throws Exception {
        for (MachineScheduling item : machineSchedulingList) {
            // 新增前再判断一次顺序是否正确
            MachineScheduling machineScheduling = machineSchedulingMapper.searchSchedulingMaxOrderByMachineId(item.getMachineId());
            if (StringUtils.isNotNull(machineScheduling)) {
                item.setSchedulingOrder(machineScheduling.getSchedulingOrder() + 1);
            } else {
                item.setSchedulingOrder(ERPConstant.SCHEDULING_MAX_PRIORITY);
            }
            machineSchedulingMapper.insertMachineScheduling(item);
        }
        return 1;
    }

    /**
     * 检查该加工单在机器上是否有排产记录
     *
     * @param workSheetId
     * @return
     */
    public boolean checkIfScheduled(String workSheetId) {
        return StringUtils.isNotNull(machineSchedulingMapper.searchMachineSchedulingByWorkSheetId(workSheetId));
    }

    /**
     * 根据状态为某加工单对象赋值，字段ifUsing true-1，false-0
     */
    public void setIfUsingForObject(MachineScheduling machineScheduling, String workSheetId, String shiftInfoId) {
        if (checkIfScheduledNowShift(shiftInfoId, workSheetId)) {
            machineScheduling.setIfUsing(ERPConstant.COMMON_YES); //当前已被排产
        } else {
            machineScheduling.setIfUsing(ERPConstant.COMMON_NO);
        }
    }

    /**
     * 检查当前班次该加工单是否被排产
     *
     * @param shiftInfoId
     * @param workSheetId
     * @return
     */
    public boolean checkIfScheduledNowShift(String shiftInfoId, String workSheetId) {
        return StringUtils.isNotNull(schedulingMapper.searchSchedulingByWorkSheetId(shiftInfoId, workSheetId));
    }

    /**
     * 寻找哈希表中的最小value对应的key
     * @param map
     * @return
     */
    public long findMachineOfMinValue(Map<String, Long> map) {
        int length = map.size();
        long[] nums = new long[length];
        int size = 0;
        for (Map.Entry<String, Long> entry : map.entrySet()) {
            nums[size++] = entry.getValue();
        }
        long min = nums[0];
        for (int i = 1; i < length; i++) {
            if (min >= nums[i]) {
                min = nums[i];
            }
        }
        return min;
    }

    /**
     * 根据map的value获取map的key
     * @param map
     * @param value
     * @return
     */
    public String getKey(Map<String, Long> map,long value){
        String key="";
        for (Map.Entry<String, Long> entry : map.entrySet()) {
            if(value == entry.getValue()){
                key=entry.getKey();
            }
        }
        return key;
    }
}
